# -*- coding: UTF-8 -*-
"""
    @Author:YTQ
    @Time: 2023/2/4 8:47
    Description:
    
"""
import json
import logging
import random
import re
import time
from urllib.parse import urlsplit, parse_qs, unquote, urlencode
import requests
from lxml import etree
from hy_base64 import hy_decode
from hy_md5 import md5


class HuYaLive:
    """
    虎牙直播视频信息
    """

    def __init__(self):
        self.content = ''
        self.url = ''
        self.user_agent = [
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"
            "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 Safari/536.11",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
            "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
            "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
            "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
            "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
            "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
            "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
            "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
        ]

    def request(self):
        """
        请求
        :return:
        """
        headers = {
            "User-Agent": random.choice(self.user_agent)
        }
        response = requests.get(self.url, params={}, headers=headers, proxies={}, timeout=15)
        # 响应结果处理
        self.content = response.text if response.status_code == requests.codes.ok else None

    def time_parse(self, time_str: str) -> str:
        """
        时间解析
        :param time_str:
        :return:
        """
        return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(int(time_str)))

    def video_url_re_parse(self, video: str) -> str:
        """
        视频地址重解析
        :param video:
        :return:
        """
        result = urlsplit(video)
        # 参数
        request_param = result.query
        param_obj = parse_qs(request_param)
        ws_time = param_obj.get('wsTime', [''])[0]
        # url
        main_url = result.path
        r = main_url.split('/')
        stream_name = re.sub('\.(flv|m3u8)$', '', r[-1])
        fm_str = unquote(param_obj.get('fm')[0])
        fm_parse = hy_decode(content=fm_str)
        p = fm_parse.split('_')[0]
        need_time = int(1e4 * time.time() + 1e4 * random.random())
        new_ws_secret = '{p}_0_{stream_name}_{need_time}_{ws_time}'.format(p=p, stream_name=stream_name,
                                                                           need_time=need_time, ws_time=ws_time)
        new_ws_secret = md5(new_ws_secret)
        request_obj = {
            'wsSecret': new_ws_secret,
            'wsTime': ws_time,
            'u': 0,
            'seqid': need_time,
            'ratio': 4000,
        }
        #
        for key, value in param_obj.items():
            if key != 'fm' and request_obj.get(key, None) is None:
                request_obj[key] = value[0]
        #
        return video.split('?')[0] + "?" + urlencode(request_obj)

    def assemble(self) -> dict:
        """
        信息组装
        :return:
        """
        if self.content is None:
            logging.error('请求失败')
            raise Exception('请求失败！')
        #
        _element = etree.HTML(self.content)
        result = str(_element.xpath('//script')[-2].text)
        if result.find('var hyPlayerConfig =') == -1:
            logging.error('请求失败，请检查网址或房间号')
            raise Exception("请检查网址或房间号")
        start_length = result.index('var hyPlayerConfig =') + 20
        end_length = result.rindex('window.TT_LIVE_TIMING') - 21
        live_data = result[start_length: end_length]
        live_data = "".join(live_data.split())
        live_data = live_data[live_data.index('stream') + 7:]
        live_data = json.loads(live_data)
        # json parse
        second_parse_data = live_data.get('data')[0]
        # 直播间信息
        game_live_info = second_parse_data.get('gameLiveInfo')
        live_room_info = {
            "uid": game_live_info.get('uid'),  # uid
            "name": game_live_info.get('nick'),  # 昵称
            "avatar": game_live_info.get('avatar180'),  # 头像
            "sex": "男" if game_live_info.get('sex') == 1 else "女",  # 性别
            "roomId": game_live_info.get('profileRoom'),  # 房间号
            "gameName": game_live_info.get('gameFullName'),  # 游戏名称
            "startTime": self.time_parse(game_live_info.get('startTime')),  # 开始时间
            "liveTitle": game_live_info.get('introduction')  # 直播标题
        }
        # cdn 源
        game_stream_info_list = second_parse_data.get('gameStreamInfoList')  # 4
        game_live_flv = []
        game_live_m3u8 = []
        #
        for game_stream_info in game_stream_info_list:
            flv_play_path = "{urlHead}/{urlMid}.{playType}?{param1}".format(urlHead=game_stream_info.get('sFlvUrl'),
                                                                            urlMid=game_stream_info.get('sStreamName'),
                                                                            playType=game_stream_info.get(
                                                                                'sFlvUrlSuffix'),
                                                                            param1=game_stream_info.get('sFlvAntiCode'),
                                                                            )
            game_live_flv.append({
                "cdnType": game_stream_info.get('sCdnType'),
                "streamName": game_stream_info.get('sStreamName'),
                "playUrl": self.video_url_re_parse(flv_play_path)
            })
            #
            m3u8_play_path = "{urlHead}/{urlMid}.{playType}?{param1}".format(urlHead=game_stream_info.get('sHlsUrl'),
                                                                             urlMid=game_stream_info.get('sStreamName'),
                                                                             playType=game_stream_info.get(
                                                                                 'sHlsUrlSuffix'),
                                                                             param1=game_stream_info.get(
                                                                                 'sHlsAntiCode'),
                                                                             )
            game_live_m3u8.append({
                "cdnType": game_stream_info.get('sCdnType'),
                "streamName": game_stream_info.get('sStreamName'),
                "playUrl": self.video_url_re_parse(m3u8_play_path)
            })
        # 清晰度
        stream_config_info = live_data.get('vMultiStreamInfo')
        video_stream_config = []
        for streamConfig in stream_config_info:
            if streamConfig.get('iBitRate') != 0:
                video_stream_config.append({
                    "name": streamConfig.get('sDisplayName'),
                    "rate": streamConfig.get('iBitRate')
                })
        # return
        return {
            "liveInfo": live_room_info,
            "flvStreamInfo": game_live_flv,
            "m3u8StreamInfo": game_live_m3u8,
            "configInfo": video_stream_config
        }

    def run(self, hy_url: str) -> dict:
        """
        运行主函数
        :param hy_url:
        :return:
        """
        self.url = hy_url
        # 请求
        self.request()
        # 组装结果
        return self.assemble()


if __name__ == '__main__':
    logging.basicConfig(format='[%(asctime)s][*%(levelname)s]:%(message)s',
                        level=logging.INFO)
    # url = "https://www.huya.com/688"
    # url = "https://www.huya.com/651353"
    url = "https://www.huya.com/521995"
    hy = HuYaLive()
    live_info = hy.run(hy_url=url)
    logging.info(live_info)
